package com.ksd.redis.util;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import lombok.NonNull;
import org.springframework.data.redis.connection.StringRedisConnection;
import org.springframework.data.redis.core.*;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * redis工具类
 *
 * @author liuzhibin
 * Date: 2019/10/24
 */
public class RedisUtil {

    private static StringRedisTemplate stringRedisTemplate = SpringContextUtil.getSpecificTypesBean(StringRedisTemplate.class);

    public static HashOperations<String, String, String> getHashOperations() {
        return stringRedisTemplate.opsForHash();
    }

    public static ValueOperations<String, String> getValueOperations() {
        return stringRedisTemplate.opsForValue();
    }

    public static ZSetOperations<String, String> getZSetOperations() {
        return stringRedisTemplate.opsForZSet();
    }

    public static StringRedisTemplate getStringRedisTemplate() {
        return stringRedisTemplate;
    }

    /**
     * 获取hash类型的value
     *
     * @param key
     * @param hashKey
     * @return
     */
    public static String getHashValue(String key, @NonNull String hashKey) {
        HashOperations<String, String, String> hashOperations = getHashOperations();
        return hashOperations.get(key, hashKey);
    }

    /**
     * 设置hashValue
     *
     * @param key
     * @param hashKey
     * @param value
     */
    public static void putHashValue(@NonNull String key, @NonNull String hashKey, @NonNull Object value) {
        HashOperations<String, String, String> hashOperations = getHashOperations();
        if (value instanceof String) {
            hashOperations.put(key, hashKey, (String) value);
        } else {
            hashOperations.put(key, hashKey, JSON.toJSONString(value, SerializerFeature.WriteNullStringAsEmpty));
        }
    }

    /**
     * 获取value对象
     *
     * @param key
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> List<T> getListValue(@NonNull String key, Class<T> clazz) {
        String value = getValue(key);
        return JSON.parseArray(value, clazz);
    }

    /**
     * 批量加入hashValue
     *
     * @param key
     * @param value
     */
    public static void putAllHashValue(@NonNull String key, @NonNull Map<String, String> value) {
        HashOperations<String, String, String> hashOperations = getHashOperations();
        hashOperations.putAll(key, value);
    }

    /**
     * 删除hashValue
     *
     * @param key
     * @param hashkey
     */
    public static void delHashValue(@NonNull String key, String... hashkey) {
        HashOperations<String, String, String> hashOperations = getHashOperations();
        hashOperations.delete(key, hashkey);
    }

    /**
     * 获取所有的hashKeys
     *
     * @param key
     * @return
     */
    public static Set<String> getHashKeys(@NonNull String key) {
        return getHashOperations().keys(key);
    }

    /**
     * 设置有效时间
     *
     * @param key
     * @param time
     * @param timeUnit
     */
    public static void setTimeout(@NonNull String key, @NonNull long time, @NonNull TimeUnit timeUnit) {
        stringRedisTemplate.expire(key, time, timeUnit);
    }

    public static long getExpire(@NonNull String key, @NonNull TimeUnit timeUnit){
        return stringRedisTemplate.getExpire(key,timeUnit);
    }

    /**
     * 设置value
     *
     * @param key
     * @param value
     */
    public static void putValue(@NonNull String key, @NonNull Object value) {
        ValueOperations<String, String> operations = getValueOperations();
        if (value instanceof String) {
            operations.set(key, (String) value);
        } else {
            operations.set(key, JSON.toJSONString(value));
        }

    }

    /**
     * 设置value和有效时间
     *
     * @param key
     * @param value
     * @param timeout  有效时间
     * @param timeUnit 时间单位
     */
    public static Boolean putValue(@NonNull String key, @NonNull Object value, @NonNull long timeout, @NonNull TimeUnit timeUnit) {
        putValue(key, value);
        return stringRedisTemplate.expire(key, timeout, timeUnit);
    }

    /**
     * 设置value
     *
     * @param key
     * @param value
     * @return 如果已存在，则返回false
     */
    public static Boolean putValueIfAbsent(@NonNull String key, @NonNull Object value) {
        ValueOperations<String, String> operations = getValueOperations();
        if (value instanceof String) {
            return operations.setIfAbsent(key, (String) value);
        } else {
            return operations.setIfAbsent(key, JSON.toJSONString(value));
        }
    }

    /**
     * 设置value
     *
     * @param key
     * @param value
     * @param timeout  有效时间
     * @param timeUnit 时间单位
     */
    public static Boolean putValueIfAbsent(@NonNull String key, @NonNull Object value, @NonNull long timeout, @NonNull TimeUnit timeUnit) {
        Boolean ret = putValueIfAbsent(key, value);
        if (ret) {
            stringRedisTemplate.expire(key, timeout, timeUnit);
        }
        return ret;
    }

    /**
     * 获取value
     *
     * @param key
     * @return
     */
    public static String getValue(@NonNull String key) {
        ValueOperations<String, String> operations = getValueOperations();
        return operations.get(key);
    }

    /**
     * 获取value对象
     *
     * @param key
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> T getValue(@NonNull String key, Class<T> clazz) {
        String value = getValue(key);
        return JSON.parseObject(value, clazz);
    }

    /**
     * 删除
     *
     * @param key
     */
    public static void delete(@NonNull String key) {
        if (hasKey(key)) {
            stringRedisTemplate.delete(key);
        }
    }

    /**
     * 删除 指定前缀
     *
     * @param key
     */
    public static void deleteBach(@NonNull String key) {
        Set<String> keys=stringRedisTemplate.keys(key+"*");
        stringRedisTemplate.delete(keys);
    }

    /**
     * 判断是否存在key
     *
     * @param key
     * @return
     */
    public static boolean hasKey(@NonNull String key) {
        return stringRedisTemplate.hasKey(key);
    }

    /**
     * 发布信息
     *
     * @param channel
     * @param message
     */
    public static void publish(@NonNull String channel, @NonNull String message) {
        stringRedisTemplate.convertAndSend(channel, message);
    }

    /**
     * 向list left push元素
     *
     * @param key
     * @param value
     */
    public static void lpush(@NonNull String key, @NonNull Object value) {
        if (value instanceof String) {
            stringRedisTemplate.opsForList().leftPush(key, (String) value);
        } else {
            stringRedisTemplate.opsForList().leftPush(key, JSON.toJSONString(value));
        }
    }

    /**
     * rpop弹出元素
     *
     * @param key
     * @return
     */
    public static String rpop(@NonNull String key) {
        return stringRedisTemplate.opsForList().rightPop(key);
    }

    /**
     * 自增1
     *
     * @param key key
     */
    public static Long increment(@NonNull String key) {
        return increment(key, 1);
    }

    /**
     * 自增
     *
     * @param key key
     */
    public static Long increment(@NonNull String key, long l) {
        return stringRedisTemplate.boundValueOps(key).increment(l);
    }

    /**
     * 上锁
     * @param key key
     * @param leaseSeconds 秒
     * @return 是否上锁成功
     */
    public static boolean look(@NonNull String key,long leaseSeconds){
        return stringRedisTemplate.opsForValue().setIfAbsent(key,"1",leaseSeconds,TimeUnit.SECONDS);
    }

    /**
     * 解锁
     * @param key key
     */
    public static void unLook(@NonNull String key){
        delete(key);
    }

    /**
     * 设置bitmap
     * @param key key
     * @param offsets 偏移量
     */
    public static void setBits(@NonNull String key, long[] offsets) {
        stringRedisTemplate.executePipelined(
                (RedisCallback<Boolean>) redisConnection -> {
                    StringRedisConnection stringRedisConn = (StringRedisConnection) redisConnection;
                    for (long offset : offsets) {
                        stringRedisConn.setBit(key, offset, true);
                    }
                    return null;
                }
        );
    }

    /**
     * 获取偏移量上面是否都为1
     * @param key key
     * @param offsets 偏移量
     */
    public static Boolean getBitsIs1(@NonNull String key,long[] offsets){
        List<Object> bits=stringRedisTemplate.executePipelined(
                (RedisCallback<Object>) redisConnection -> {
                    StringRedisConnection stringRedisConn = (StringRedisConnection) redisConnection;
                    for(long offset:offsets){
                        stringRedisConn.getBit(key,offset);
                    }
                    return null;
                }
        );
        if(bits == null){
            return false;
        }
        if(bits.stream().anyMatch(res-> !(Boolean) res)){
            return false;
        }
        return true;
    }

}
